package com.powernode.config;

import com.powernode.authorization.AuthorizationManager;
import com.powernode.component.CustomAccessDeniedHandler;
import com.powernode.component.CustomAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;

/**
 * 资源服务器配置
 * 因为资源服务器在网关部署，springcloud的网关是基于webFlux的，所以使用@EnableWebFluxSecurity注解
 * 不能使用@EnableWebSecurity
 */
@Configuration
@EnableWebFluxSecurity
public class ResourceServerConfig {

    @Autowired
    private AuthorizationManager authorizationManager;
    @Autowired
    private CustomAccessDeniedHandler accessDeniedHandler;
    @Autowired
    private CustomAuthenticationEntryPoint entryPoint;

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http){
        // 指定资源服务器
        http.oauth2ResourceServer()
                // 指定使用jwt传输token
                .jwt()
                // 指定jwt认证转换器（自定义实现）
                .jwtAuthenticationConverter(jwtAuthenticationConverter())
                // 指定jwt公钥获取地址（使用公钥对jwt串进行解析）
                .jwkSetUri("http://localhost:7777/rsa/publicKey")
                .and()
                // 设置认证失败处理器
                .authenticationEntryPoint(entryPoint)
                // 设置鉴权失败处理器
                .accessDeniedHandler(accessDeniedHandler)
                .and()
                .authorizeExchange()
                // 配置白名单：用户登录时获取token的路径
                .pathMatchers("/oauth/token").permitAll()
                // 配置白名单：商品微服务下app端的路径，用户不用登陆和鉴权即可浏览商品
                .pathMatchers("/pms/app/**").permitAll()
                // 配置白名单：首页内容微服务下的路径，用户不用登陆和鉴权即可浏览首页内容
                .pathMatchers("/cms/app/**").permitAll()
                // 配置白名单：商品搜索微服务下的路径，用户不用登录和鉴权即可搜索商品
                .pathMatchers("/search/app/**").permitAll()
                // 配置白名单：用户微服务下获取短信验证码的路径
                .pathMatchers("/ums/app/appUsers/loginCode/*").permitAll()
                // 配置其他路径的鉴权管理器
                .pathMatchers("/**").access(authorizationManager)
                .anyExchange().authenticated()
                // 关闭跨站请求伪造的防护
                .and().csrf().disable();
        return http.build();
    }

    /**
     * ServerHttpSecurity默认没有将jwt的payloader负载部分的authorities内容当作Authentication的内容
     * 需要重新定义ReactiveAuthenticationManager的转换器JwtGrantedAuthenticationConverter
     * 将jwt的claim中的authorities内容加入进去
     */
    @Bean
    public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        // 设置权限的前缀
        grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
        // 设置权限的名称
        grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");

        JwtAuthenticationConverter authenticationConverter = new JwtAuthenticationConverter();
        authenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);

        return new ReactiveJwtAuthenticationConverterAdapter(authenticationConverter);
    }
}
